/**
 * 
 */
package com.ap.core.bulkupload.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ap.core.bulkupload.constants.BulkFileUploadConstants;
import com.ap.core.bulkupload.jaxb.domain.MerchantRegBulkUploadResponseType;
import com.ap.core.exception.APCoreException;

/**
 * The Class XMLWriterUtil.
 *
 * @author Dilip.Sridhar
 */
public class XMLWriterUtil {
	
	/** The Constant log. */
	private static final Logger log = LoggerFactory.getLogger(XMLWriterUtil.class);
	
	/**
	 * Gets the single instance of XMLWriterUtil.
	 *
	 * @return single instance of XMLWriterUtil
	 */
	public static XMLWriterUtil getInstance() {
		return new XMLWriterUtil();
	}
	
	/**
	 * Write to xml file.
	 *
	 * @param bulkuploadResponse the bulkupload response
	 * @param inputFilePath the input file path
	 * @return the file
	 * @throws APCoreException the aP core exception
	 */
	public File writeToXmlFile(MerchantRegBulkUploadResponseType bulkuploadResponse, String inputFilePath) throws APCoreException {
		File file = null;
		try {
			
			/* TODO:following logic should be modified to implement file output stream way in the enhanced story*/
			log.info("inputFilePath :"+inputFilePath);
			//inputFilePath = FilenameUtils.normalize(inputFilePath);
			log.info("inputFilePath :"+inputFilePath);
			StringWriter writer = javaToXMLForDist(MerchantRegBulkUploadResponseType.class, bulkuploadResponse);
			File baseDir = this.createDirectory(inputFilePath, bulkuploadResponse.getDistributorId().getValue(), "processed");

			String currentTimeStamp =  new LocalDateTime().toString().replace(":", "_");
			String outputFilePath = baseDir.getPath() + File.separator +"Bulk_Processed_Merchants_"+ currentTimeStamp +".xml";
			
			Path path = Files.write(Paths.get(outputFilePath), writer.toString().getBytes());
			file = path.toFile();
			
		} catch (IOException e) {
			log.error("IO Exception Reading the file in the path:"+e.getMessage());
			throw new APCoreException(e);
		} catch (Exception e) {
			log.error("Exception in the file in the path:"+e.getMessage());
			throw new APCoreException(e);
		}
		
		return file;		
	}
	
    /**
     * Java to xml for dist.
     *
     * @param clazz the clazz
     * @param value the value
     * @return the string writer
     * @throws APCoreException the aP core exception
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private StringWriter javaToXMLForDist(Class clazz, Object value) throws APCoreException {
        StringWriter sw = new StringWriter();
        try {
            if (value == null || clazz == null) {
                log.info("either clazz or value is emply");
            }

            JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            jaxbMarshaller.marshal(new JAXBElement<Object>(new QName(BulkFileUploadConstants.RESPONSE_QNAME), clazz, value), sw);
            log.info("Marshalled Msg :\n " + sw.toString());
            
            return sw;
            
        } catch (JAXBException ex) {
             log.error("JAXBException occurred Message [" + ex.getMessage() + "] and Cause [" + ex.getCause() + "]");
             throw new APCoreException(ex);
        } catch (Exception e) {
			log.error("Exception occurred  ["+e.getMessage()+"]");
			throw new APCoreException(e);			
		}
       
    }

    
    /**
     * Archive file.
     *
     * @param sourceFile the source file
     * @param distributorId the distributor id
     * @throws APCoreException the aP core exception
     */
    public void archiveFile(String sourceFile, String distributorId) throws APCoreException {
    	
    	byte[] buffer = new byte[1024];
    	int length = 0;
    	
    	FileInputStream fis = null;
    	FileOutputStream fos = null;
    	
    	try {
    		log.info(" File archive started ...");
    		log.info("inputFilePath :"+sourceFile+"-- distributorId:"+distributorId);
    		//sourceFile = FilenameUtils.normalize(sourceFile);
    		log.info("after inputFilePath :"+sourceFile+"-- distributorId:"+distributorId);
    		File file = new File(sourceFile);
    		File archiveddDir = this.createDirectory(sourceFile, distributorId, "archived");

    		String tempName = file.getName().substring(0, file.getName().lastIndexOf(".xml"));
			String currentTimeStamp =  new LocalDateTime().toString().replace(":", "_");
			String destFile = archiveddDir + File.separator +tempName+"_"+currentTimeStamp+".xml";
			
			 fis = new FileInputStream(file);
			 fos = new FileOutputStream(new File(destFile));
			 
			try {
				while((length = fis.read(buffer)) > 0) {
					fos.write(buffer, 0, length);
				}
				IOUtils.closeQuietly(fis);
				IOUtils.closeQuietly(fos);

				file.delete();
				log.info(" File archive completed ...");
			} catch (IOException e) {
				log.error("IOException occurred  ["+e.getMessage()+"]");
				throw new APCoreException(e);
			}
			
		} catch (FileNotFoundException e) {
			log.error("FileNotFoundException occurred  ["+e.getMessage()+"]");
			throw new APCoreException(e);
		} catch (Exception e) {
			log.error("Exception occurred  ["+e.getMessage()+"]");
			throw new APCoreException(e);			
		}
    }
    
    
    /**
     * Creates the directory.
     *
     * @param inputFilePath the input file path
     * @param distributorId the distributor id
     * @param directoryType the directory type
     * @return the string
     * @throws Exception the exception
     */
    private File createDirectory(String inputFilePath, String distributorId, String directoryType) throws Exception {
    	int uploadIndex = 0;
		log.info("inputFilePath :"+inputFilePath+"-- distributorId:"+distributorId+"-- directoryType:"+directoryType);
		if(inputFilePath.contains(File.separator +"upload"+ File.separator)) {
			uploadIndex = inputFilePath.lastIndexOf(File.separator +"upload"+ File.separator);
		} 
		
		String baseDir = inputFilePath.substring(0, uploadIndex);			
		baseDir = baseDir + File.separator + directoryType;
		
		File dirPath = new File(baseDir);
        if(!dirPath.exists()){
        	dirPath.mkdir();
        }    
        
        baseDir = baseDir + File.separator + distributorId;
        dirPath = new File(baseDir);
        if(!dirPath.exists()){
        	dirPath.mkdir();
        }
        
        return dirPath;
    }
}
